package ru.hangman.donkeykong.persons;

import java.util.ArrayList;

import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;

import ru.hangman.donkeykong.logic.LevelRepresentation;

import android.content.Context;
import android.graphics.Point;
import android.util.Log;

/**
 * Base class for any person in game.
 * @author pavel.todorov
 */
public abstract class DonkeyKongPerson {

	/**
	 * Class for saving texture and it's region for person.
	 * @author pavel.todorov
	 */
	private class TexturesData {
		BitmapTextureAtlas _atlas;
		TiledTextureRegion _region;
		long _animationDuration;	//!< It's option for animation velocity.
		Point _offset;				//!< Offset for the sprite then it's draw.
		
		/**
		 * Constructor.
		 * @param context Current context.
		 * @param width Width for image resource. Must be power of 2.
		 * @param height Height for image resource. Must be power of 2.
		 * @param path Path for image resource.
		 * @param tileColumns Number of columns in sprite representation.
		 * @param tileRows Number of rows in sprite representation.
		 * @param animationDuration Animation frame's duration time.
		 * @param offset Offset for sprite picture.
		 */
		public TexturesData(Context context, int width, int height, String path, int tileColumns, int tileRows, 
				long animationDuration, Point offset) {
			_atlas = new BitmapTextureAtlas(width, height, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
			_region = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(
	        		_atlas, context, path, 0, 0, tileColumns, tileRows);
			_animationDuration = animationDuration;
			_offset = offset;
		}
		
		/**
		 * Get sprite atlas.
		 * @return Atlas.
		 */
		BitmapTextureAtlas getAtlas() {
			return _atlas;
		}
		
		/**
		 * Get sprite representation.
		 * @return Representation.
		 */
		TiledTextureRegion getRegion() {
			return _region;
		}
		
		/**
		 * Get animation duration.
		 * @return Duration in ms.
		 */
		long getAnimationDuration() {
			return _animationDuration;
		}
		
		/**
		 * Get sprite's offset.
		 * @return Offset point.
		 */
		Point getOffset() {
			return _offset;
		}
	}
	
	///List of textures data.
	private ArrayList<TexturesData> texturesData = new ArrayList<TexturesData>();
	
	///Offset for game activity where game person is.
	private BaseGameActivity _activity;
	
	///Current sprite for person.
	AnimatedSprite _sprite = null;
	
	///Person position.
	Point _position = new Point(0, 0);
	
	///Last flipping flag value.
	boolean _flipping = false;

	///Last sprite id.
	int _lastId = -1;
	
	///Current sprite id.
	int _currentId = 0;
	
	/**
	 * Constructor.
	 * @param activity Game activity for person.
	 */
    protected DonkeyKongPerson(BaseGameActivity activity) {
    	_activity = activity;
    }
    
    /**
     * Initialize new texture (sprite) for person.
     * @param width Texture width.
     * @param height Texture height.
     * @param path Path to texture image.
     * @param tileColumns Number of columns in sprite representation.
     * @param tileRows Number of rows in sprite representation.
     * @param animationDuration Animation duration time for sprite.
     * @param offset Sprite offset.
     * @return Id for initialized sprite.
     */
    protected int initTexture(int width, int height, String path, int tileColumns, int tileRows, 
    		long animationDuration, Point offset) {
    	TexturesData data = new TexturesData(_activity, width, height, path, tileColumns, tileRows, animationDuration, offset);
    	texturesData.add(data);
        _activity.getEngine().getTextureManager().loadTexture(data.getAtlas());
        return texturesData.size()-1;
    }
    
    /**
     * Draw sprite on scene.
     * @param scene Scene.
     */
    protected void redraw(final Scene scene) {
    	if(scene == null)
    		return;

    	TiledTextureRegion region = texturesData.get(_currentId).getRegion();
    	Point offset = texturesData.get(_currentId).getOffset();
    	if((_currentId != _lastId) || (_sprite == null)) {
    		if(_sprite != null) {
    			_sprite.setCurrentTileIndex(0);
    			scene.detachChild(_sprite);
    		}
    	
	        _sprite = new AnimatedSprite(_position.x - region.getTileWidth()/2 + offset.x, 
	        		_position.y - region.getTileHeight()/2 + offset.y, region);
	        _sprite.setFlippedHorizontal(_flipping);
	        _sprite.animate(texturesData.get(_currentId).getAnimationDuration());
	        _sprite.setZIndex(1);
	        scene.attachChild(_sprite);
	        
	    	_lastId = _currentId;
    	}
    	else {
	        _sprite.setFlippedHorizontal(_flipping);
	        _sprite.setPosition(_position.x - region.getTileWidth()/2 + offset.x, 
	        		_position.y - region.getTileHeight()/2 + offset.y);
    	}
    }

    /**
     * Set new sprite id for person.
     * @param id New id.
     */
    public void setId(int id) {
    	if(_currentId == id)
    		return;
    	_lastId = _currentId;
    	_currentId = id;
    }
    
    /**
     * Set new sprite position.
     * @param position New position.
     */
    public void setNewPosition(Point position) {
    	Log.d("DonkeyKongPerson", String.format("New position: (%s)", position.toString()));
    	
    	_position.x = position.x;
    	_position.y = position.y;
    }
    
    /**
     * Make sprite flipping on/off.
     * @param flag On flag.
     */
    protected void turnFlippingOn(boolean flag) {
    	_flipping = flag;
    	_sprite.setFlippedHorizontal(flag);
    }
    
    /**
     * Get current person position on the screen (not the level).
     * @return Current person position.
     */
    public Point getCurrentPosition() {
    	return _position;
    }
    
    /**
     * Make person reaction for new step position.
     * @param sourcePoint From which point has step made. 
     * @param pointDescription Destination step point for processing.
     * @param screenOffset If >= 0 it's screen offset to set person, if < 0 - it's don't used option.
     * @return true if scene update needed, false overwise.
     */
    abstract public boolean processStepPointDescription(Point sourcePoint, LevelRepresentation.PointDescription pointDescription, int screenOffset); 
}
